﻿using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.Design;
using System.Web.UI.WebControls;
using System.Reflection;
using System.ComponentModel;
using System.Security;
using System.Security.Permissions;

using PickGold.Util;

using HttpJson = PickGold.Web.Json;

namespace PickGold.Web.Controls
{
	/// <summary>
	/// 权限授权，页面可继承【IGrantTester】接口实现自动绑定，否则应显式指定【AllowIndex】属性或绑定【Tester】属性。
	/// </summary>
	//[AspNetHostingPermission(SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Minimal)]
	//[ParseChildren(false)]
	//[PersistChildren(true)]
	//[Designer(typeof(Designer))]
	[Bindable(true)]
	[ControlBuilder(typeof(Builder))]
	[DefaultEvent("BeginTest")]
	[DefaultProperty("Key")]
	[DisplayName("权限授权点")]
	[Description("权限授权，页面可继承【IGrantTester】接口实现自动绑定，否则应显式指定【AllowIndex】属性或绑定【Tester】属性。")]
	[ToolboxData("<{0}:Grant runat=\"server\" />")]
	public partial class Grant : Control, IGrantPoint, IAjaxInvoker
	{
		private static readonly object AjaxHandlerKey = new object();
		private static readonly object BeginTestHandlerKey = new object();
		private static readonly object AfterTestHandlerKey = new object();

		private Guid _Uuid = Guid.Empty;
		private int _Allow = 0;
		private int _Deny = 1;

		/// <summary>
		/// 权限检测器，而页可继承此接口自动实现绑定。
		/// </summary>
		[DisplayName("权限检测器")]
		[Description("权限检测器，而页可继承此接口自动实现绑定。")]
		[Browsable(false)]
		public IGrantTester Tester { set; get; }

		/// <summary>
		/// 授权键。
		/// </summary>
		[Bindable(true)]
		[DisplayName("授权键")]
		[Description("授权键。")]
		public string Key { set; get; }

		/// <summary>
		/// 授权键。
		/// </summary>
		[Bindable(true)]
		[DisplayName("授权键")]
		[Description("授权键。")]
		[Browsable(false)]
		[EditorBrowsable(EditorBrowsableState.Advanced)]
		public string Name { get { return this.Key; } }

		/// <summary>
		/// 授权数据。
		/// </summary>
		[Bindable(true)]
		[DisplayName("授权数据")]
		[Description("授权数据。")]
		public string Data { set; get; }

		/// <summary>
		/// 授权值。
		/// </summary>
		[Bindable(true)]
		[DisplayName("授权值")]
		[Description("授权值。")]
		public decimal Value { set; get; }

		/// <summary>
		/// 授权组。
		/// </summary>
		[Bindable(true)]
		[DisplayName("授权组")]
		[Description("授权组，仅对【AllowIndex】设置有效。")]
		public string Group { set; get; }

		/// <summary>
		/// 默认授权索引；在权限测试接口实现中无需赋值此属性，而是返回新值。
		/// </summary>
		[Bindable(true)]
		[DefaultValue(0)]
		[DisplayName("默认授权索引")]
		[Description("默认授权的子控件索引，会同步组内所有成员的【AllowIndex】属性。")]
		public int AllowIndex
		{
			set
			{
				if (this._Allow == value)
					return;

				this._Allow = value;
				if (string.IsNullOrEmpty(this.Group) || this.Page == null || this.Page.Items == null)
					return;

				foreach (var item in base.Page.Items.Values)
				{
					var grant = item as IGrantPoint;
					if (grant != null && grant != this && grant.Group == this.Group && grant.AllowIndex != value)
						grant.AllowIndex = value;
				}
			}
			get
			{
				return this._Allow;
			}
		}

		/// <summary>
		/// 默认拒绝索引。
		/// </summary>
		[Bindable(true)]
		[DefaultValue(1)]
		[DisplayName("默认拒绝索引")]
		[Description("默认拒绝的子控件索引。")]
		public int DenyIndex
		{
			set
			{
				this._Deny = value;
			}
			get
			{
				return this._Deny;
			}
		}

		/// <summary>
		/// 拒绝当前权限点；将当前拒绝索引赋给授权索引。
		/// </summary>
		public void Deny()
		{
			this.AllowIndex = this.DenyIndex;
		}

		bool IAjaxInvoker.AjaxInvoke(string[] parameters)
		{
			var e = new AjaxEventArgs(this, parameters);
			this.OnAjax(e);
			return e.ResultWrap.IsCompleted;
		}

		/// <summary>
		/// 引发Ajax调用事件。
		/// </summary>
		/// <param name="e">事件参数。</param>
		protected virtual void OnAjax(AjaxEventArgs e)
		{
			if (e.ResultWrap.IsCompleted)
				return;

			var p = this.Page as WebPage;
			var a = new GrantEventArgs(this);
			var tester = this.Tester;
			if (tester == null)
				tester = p;
			this.OnBeginGrantTest(a);
			if (tester != null)
				a.Result = tester.GetGrantAllow(this);
			this.OnAfterGrantTest(a);
			if (a.Result != this._Allow)
			{
				var context = this.Context;
				if (context == null)
					context = HttpContext.Current;
				context.Response.AddHeader(this.GetType().FullName + "::" + this.ID, HttpStatus.Message.UNAUTHORIZED);
				if (p == null)
				{
					if (this.Page != null)
						context.Response.AddHeader(this.GetType().FullName + "::" + this.ClientID, this.Page.GetType().FullName);
					context.Write(HttpStatus.Code.Unauthorized);
					context.Response.End();//特殊情况，此控件未运行在本技术框架（WebPage）内，用终止方法。
					return;
				}

				p.OutContent = HttpStatus.Code.Unauthorized;
				return;
			}

			var h = base.Events[Grant.AjaxHandlerKey] as AjaxEventHandler;
			if (h != null)
			{
				h(this, e);
			}
			else if (p != null)
			{
				if (p.InvokeAjax(e))
					e.Completed = true;
			}
			if (e.ResultWrap.WrapResult != null)//如果是异步操作
				p.RegisterAjaxAsyncTask(null, e);
			if (!e.ResultWrap.IsCompleted)
				return;

			if (p == null)
				return;

			var content = p.OutContent;
			if (content == null)
				p.OutContent = this;
			else if (content != this)
				this.Visible = false;
		}

		/// <summary>
		/// 引发开始检测事件。
		/// </summary>
		/// <param name="e">事件参数。</param>
		protected virtual void OnBeginGrantTest(GrantEventArgs e)
		{
			var h = base.Events[Grant.BeginTestHandlerKey] as GrantEventHandler;
			if (h != null)
				h(this, e);
		}

		/// <summary>
		/// 引发结束检测事件。
		/// </summary>
		/// <param name="e">事件参数。</param>
		protected virtual void OnAfterGrantTest(GrantEventArgs e)
		{
			var h = base.Events[Grant.AfterTestHandlerKey] as GrantEventHandler;
			if (h != null)
				h(this, e);
		}

		/// <summary>
		/// Ajax调用事件。
		/// </summary>
		[DisplayName("Ajax调用事件")]
		[Description("Ajax调用事件。")]
		public event AjaxEventHandler Ajax
		{
			add
			{
				if (string.IsNullOrEmpty(this.ID) && value != null)
					this.ID = value.Method.Name;
				this.Events.AddHandler(Grant.AjaxHandlerKey, value);
			}
			remove
			{
				this.Events.RemoveHandler(Grant.AjaxHandlerKey, value);
			}
		}

		/// <summary>
		/// 开始检测事件。
		/// </summary>
		[DisplayName("开始检测事件")]
		[Description("开始检测事件。")]
		public event GrantEventHandler BeginGrantTest
		{
			add
			{
				this.Events.AddHandler(Grant.BeginTestHandlerKey, value);
			}
			remove
			{
				this.Events.RemoveHandler(Grant.BeginTestHandlerKey, value);
			}
		}

		/// <summary>
		/// 结束检测事件。
		/// </summary>
		[DisplayName("结束检测事件")]
		[Description("结束检测事件。")]
		public event GrantEventHandler AfterGrantTest
		{
			add
			{
				this.Events.AddHandler(Grant.AfterTestHandlerKey, value);
			}
			remove
			{
				this.Events.RemoveHandler(Grant.AfterTestHandlerKey, value);
			}
		}

		/// <summary>
		/// 获取对包含服务器控件的 System.Web.UI.Page 实例的引用。
		/// </summary>
		public override Page Page
		{
			get
			{
				return base.Page;
			}
			set
			{
				if (base.Page == value)
					return;

				this.SetAjax(value);
				if (this._Uuid == Guid.Empty)
					this._Uuid = Guid.NewGuid();
				if (base.Page != null)
					base.Page.Items.Remove(this._Uuid);
				base.Page = value;
				base.Page.Items[this._Uuid] = this;
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="page">新页面</param>
		/// <param name="id">新Id</param>
		private void SetAjax(Page page = null, string id = null)
		{
			var context = this.Context;
			if (context == null)
				context = HttpContext.Current;
			var t = context.Items[WebUtil.AJAX_KEY] as string;
			if (string.IsNullOrEmpty(t))
				return;

			t = context.Items[t] as string;
			if (string.IsNullOrEmpty(t))
				return;

			if (page == null)
				page = this.Page;
			if ((!string.IsNullOrEmpty(id) && string.Compare(t, id, StringComparison.OrdinalIgnoreCase) == 0) ||
				(!string.IsNullOrEmpty(this.ID) && string.Compare(t, this.ID, StringComparison.OrdinalIgnoreCase) == 0) ||
				(!string.IsNullOrEmpty(this.UniqueID) && string.Compare(t, this.UniqueID, StringComparison.OrdinalIgnoreCase) == 0) ||
				(!string.IsNullOrEmpty(this.ClientID) && string.Compare(t, this.ClientID, StringComparison.OrdinalIgnoreCase) == 0))
				page.Items[t] = this;
		}

		/// <summary>
		/// 引发 System.Web.UI.Control.Init 事件。
		/// </summary>
		/// <param name="e">包含事件数据的 System.EventArgs 对象。</param>
		protected override void OnInit(EventArgs e)
		{
			//this._Uuid = Guid.Empty;
			//this._Allow = 0;
			//this.DenyIndex = 1;
			this.SetAjax();
			base.OnInit(e);
		}

		/// <summary>
		/// 引发 System.Web.UI.Control.PreRender 事件。
		/// </summary>
		/// <param name="e">包含事件数据的 System.EventArgs 对象。</param>
		protected override void OnPreRender(EventArgs e)
		{
			var ce = new GrantEventArgs(this);
			var count = this.Controls.Count;
			var tester = this.Tester;
			if (tester == null && this.Page != null)
				tester = this.Page as IGrantTester;
			this.OnBeginGrantTest(ce);
			if (tester != null)
				ce.Result = tester.GetGrantAllow(this);
			this.OnAfterGrantTest(ce);
			for (var i = 0; i < count; i++)
			{
				var c = this.Controls[i];
				if (c == null || c is LiteralControl || c is Grant)//if (this.Controls[i].Parent != this || this.Controls[i] is LiteralControl)
				{
					ce.Result++;
					continue;
				}

				if (ce.Result != i)//PlaceHolder;Panel;
					c.Visible = false;
				//if (ce.Result == i)//PlaceHolder;Panel;
				//    c.Visible = true;
				//else
				//    c.Visible = false;
			}
			base.OnPreRender(ce);
		}

		/// <summary>
		/// 将此控件内容发送到提供的 System.Web.UI.HtmlTextWriter 对象，此对象编写将在客户端呈现的内容。
		/// </summary>
		/// <param name="writer">接收控件内容的 System.Web.UI.HtmlTextWriter 对象。</param>
		protected  void overrideRender(HtmlTextWriter writer)
		{
			var e = new GrantEventArgs(this);
			var count = this.Controls.Count;
			var tester = this.Tester;
			if (tester == null && this.Page != null)
				tester = this.Page as IGrantTester;
			this.OnBeginGrantTest(e);
			if (tester != null)
				e.Result = tester.GetGrantAllow(this);
			this.OnAfterGrantTest(e);
			if (count <= 1)
			{
				if (e.Result == 0)
					base.Render(writer);// base.RenderChildren(writer);
				return;
			}

			var bs = new byte[count];
			for (var i = 0; i < count; i++)
			{
				var c = this.Controls[i];
				if (c == null || c is LiteralControl || c is Grant)//if (this.Controls[i].Parent != this || this.Controls[i] is LiteralControl)
				{
					e.Result++;
					bs[i] = byte.MinValue;
					continue;
				}
				
				bs[i] = c.Visible ? byte.MaxValue : BitUtil.B08_00;//writer.Write(i + ":" + index + ":" + this.Controls[i].GetType().FullName + "<br />");
				if (e.Result == i)//PlaceHolder;Panel;
					c.Visible = true;
				else
					c.Visible = false;
			}
			base.Render(writer);// base.RenderChildren(writer);
			for (var i = 0; i < count; i++)
			{
				if (bs[i] > byte.MinValue)
					this.Controls[i].Visible = bs[i] == byte.MaxValue;
			}
		}
	}

	partial class Grant
	{

		/// <summary>
		/// 获取或设置分配给服务器控件的编程标识符。
		/// </summary>
		[Obsolete]
		private  string overrideID
		{
			get
			{
				return base.ID;
			}
			set
			{
				if (base.ID == value)
					return;

				this.SetAjax(id: value);
				base.ID = value;
			}
		}

		/// <summary>
		/// 由 ASP.NET 页面框架调用，以通知使用基于合成的实现的服务器控件创建它们包含的任何子控件，以便为回发或呈现做准备。
		/// </summary>
		[Obsolete]
		private void overrideCreateChildControls()
		{
			this.SetAjax();
			base.CreateChildControls();
		}

		/// <summary>
		/// 创建一个新的 System.Web.UI.ControlCollection 对象来保存服务器控件的子控件（包括文本控件和服务器控件）。
		/// </summary>
		/// <returns>包含当前服务器控件的子服务器控件的 System.Web.UI.ControlCollection 对象。</returns>
		[Obsolete]
		private ControlCollection overrideCreateControlCollection()
		{
			this.SetAjax();
			return base.CreateControlCollection();
		}

		/// <summary>
		/// 从 System.Web.UI.Control.SaveControlState() 方法保存的上一个页请求还原控件状态信息。
		/// </summary>
		/// <param name="savedState">表示要还原的控件状态的 System.Object。</param>
		[Obsolete]
		private void overrideLoadControlState(object savedState)
		{
			this.SetAjax();
			base.LoadControlState(savedState);
		}
	}
}
